/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.alertnetusa.validator;

import com.incident.cap.*;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI;
import java.util.StringTokenizer;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

/** Parses a CAP 1.1 or CAP 1.0 XML message into an EasCapAlert object<p>
 * <i>Copyright (c) 2008-2009 Thai Van<p>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * @see EasCapAlert
 * @author Thai Van, Prafash Kumar. This class is based on Art Botterell, David R Robinson work on com.incident.cap.Alert
 * @version 1 Oct 2008
 */
public class EasCapParser {

    /** all EASCAPAlert fields will be held here after CAP parsing */
    protected EasCapAlert alert;
    /** all EASCAPInfo fields will be held here after CAP parsing  */
    protected EasCapInfo info;
     /** all EasCapResource fields will be held here after CAP parsing  */
    protected EasCapResource res;
    /** all EasCapArea fields will be held here after CAP parsing  */
    protected EasCapArea area;
    /** ValuePair will be used throug ou this class to help process XML fields with <valueName> and <value> */
    protected ValuePair valuePair;
    /** String buffer for use through out this class */
    private StringBuffer buf = new StringBuffer();
    /** StringTokenizer for use through out this class */
    private StringTokenizer st;

    /** Returns an EasCapAlert object from a CAP message at a URI */
    public EasCapAlert parseFromUri(URI uri) {
        alert = new EasCapAlert();
        String uriString = uri.toString();
        try {
            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
            SAXParser parser = parserFactory.newSAXParser();
            CAPContentHandler handler = new CAPContentHandler();
            parser.parse(uriString, handler);
        } catch (Exception e) {
            e.printStackTrace();
            alert.setStatus("System");
            alert.setMsgType("Error");
            alert.setNote("XML PARSING ERROR: " + e);
        }
        return alert;
    }

    /** Returns an EasCapAlert object from a CAP message at a URI 
     * @deprecated Use parseFromUri( URI ) instead
     */
    public EasCapAlert unmarshalUri(URI uri) {
        return parseFromUri(uri);
    }

    /** Returns an EasCapAlert object from an XML string */
    public EasCapAlert parseFromString(String xml) {
        alert = new EasCapAlert();
        ByteArrayInputStream in = new ByteArrayInputStream(xml.getBytes());
        try {
            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
            SAXParser parser = parserFactory.newSAXParser();
            CAPContentHandler handler = new CAPContentHandler();
            parser.parse((InputStream) in, handler);
        } catch (Exception e) {
            e.printStackTrace();
            alert.setStatus("System");
            alert.setMsgType("Error");
            alert.setNote("XML PARSING ERROR: " + e);
        }
        return alert;
    }

    /** Returns an EasCapAlert object from an XML string 
     * @deprecated Use parseFromString( String) instead
     */
    public EasCapAlert unmarshalString(String s) {
        return parseFromString(s);
    }

    private class CAPContentHandler extends DefaultHandler {

        String version = "1.0";  // default

        public void startElement(String namespaceURI, String localname, String qname,
                Attributes attributes) {

            // remove any namespace ids
            qname = qname.replaceFirst(".*:", "");

            // detect CAP version
            if (qname.equals("alert")) {
                int length = attributes.getLength();
                for (int i = 0; i < length; i++) {
                    if (attributes.getValue(i).equals("urn:oasis:names:tc:emergency:cap:1.1")) {
                        version = "1.1";
                    }
                }
            }

            // set up subordinate data structures as required       
            if (qname.equals("info")) {
                info = new EasCapInfo();
                alert.addInfo(info);
            }
            if (qname.equals("resource")) {
                res = new EasCapResource();
                info.addResource(res);
            }
            if (qname.equals("area")) {
                area = new EasCapArea();
                info.addArea(area);
            }
            if (qname.equals("valueName")) {
                valuePair = new ValuePair();
            }

            buf.setLength(0);
        }

        public void characters(char[] ch, int start, int length) {
            buf.append(ch, start, length);
        }

        public void endElement(String uri, String localname, String qname) {

            String value = buf.toString();

            // remove any namespace ids
            qname = qname.replaceFirst(".*:", "");

            if (version.equals("1.0")) {

                if (qname.equals("identifier")) {
                    alert.setIdentifier(value);
                }
                if (qname.equals("sender")) {
                    alert.setSender(value);
                }
                if (qname.equals("sent")) {
                    alert.setSent(value);
                }
                if (qname.equals("status")) {
                    alert.setStatus(value);
                }
                if (qname.equals("scope")) {
                    alert.setScope(value);
                }
                if (qname.equals("msgType")) {
                    alert.setMsgType(value);
                }
                if (qname.equals("password")) {
                    alert.setPassword(value);
                }
                if (qname.equals("source")) {
                    alert.setSource(value);
                }
                if (qname.equals("restriction")) {
                    alert.setRestriction(value);
                }
                if (qname.equals("addresses")) {
                    alert.setAddresses(value);
                }
                if (qname.equals("code")) {
                    alert.addCode(value);
                }
                if (qname.equals("note")) {
                    alert.setNote(value);
                }
                if (qname.equals("references")) {
                    alert.setReferences(value);
                }
                if (qname.equals("incidents")) {
                    alert.setIncidents(value);
                }

                if (qname.equals("language")) {
                    info.setLanguage(value);
                }
                if (qname.equals("category")) {
                    info.addCategory(value);
                }
                if (qname.equals("event")) {
                    info.setEvent(value);
                }
                if (qname.equals("urgency")) {
                    info.setUrgency(value);
                }
                if (qname.equals("severity")) {
                    info.setSeverity(value);
                }
                if (qname.equals("certainty")) {
                    info.setCertainty(value);
                }
                if (qname.equals("audience")) {
                    info.setAudience(value);
                }
                if (qname.equals("eventCode")) {
                    try {
                        st = new StringTokenizer(value, "=");
                        info.addEventCode(st.nextToken(), st.nextToken());
                    } catch (Exception e) {
                    }
                }
                if (qname.equals("effective")) {
                    info.setEffective(value);
                }
                if (qname.equals("onset")) {
                    info.setOnset(value);
                }
                if (qname.equals("expires")) {
                    info.setExpires(value);
                }
                if (qname.equals("senderName")) {
                    info.setSenderName(value);
                }
                if (qname.equals("headline")) {
                    info.setHeadline(value);
                }
                if (qname.equals("description")) {
                    info.setDescription(value);
                }
                if (qname.equals("instruction")) {
                    info.setInstruction(value);
                }
                if (qname.equals("web")) {
                    info.setWeb(value);
                }
                if (qname.equals("contact")) {
                    info.setContact(value);
                }
                if (qname.equals("parameter")) {
                    try {
                        st = new StringTokenizer(value, "=");
                        info.addParameter(st.nextToken(), st.nextToken());
                    } catch (Exception e) {
                    }
                }

                if (qname.equals("resourceDesc")) {
                    res.setResourceDesc(value);
                }
                if (qname.equals("mimeType")) {
                    res.setMimeType(value);
                }
                if (qname.equals("size")) {
                    res.setSize(value);
                }
                if (qname.equals("uri")) {
                    res.setUri(value);
                }
                if (qname.equals("digest")) {
                    res.setDigest(value);
                }

                if (qname.equals("areaDesc")) {
                    area.setAreaDesc(value);
                }
                if (qname.equals("polygon")) {
                    area.addPolygon(value);
                }
                if (qname.equals("circle")) {
                    area.addCircle(value);
                }
                if (qname.equals("geocode")) {
                    try {
                        st = new StringTokenizer(value, "=");
                        area.addGeocode(st.nextToken(), st.nextToken());
                    } catch (Exception e) {
                    }
                }
                if (qname.equals("altitude")) {
                    area.setAltitude(value);
                }
                if (qname.equals("ceiling")) {
                    area.setCeiling(value);
                }

            } else { //default is CAP 1.1

                if (qname.equals("identifier")) {
                    alert.setIdentifier(value);
                }
                if (qname.equals("sender")) {
                    alert.setSender(value);
                }
                if (qname.equals("sent")) {
                    alert.setSent(value);
                }
                if (qname.equals("status")) {
                    alert.setStatus(value);
                }
                if (qname.equals("scope")) {
                    alert.setScope(value);
                }
                if (qname.equals("msgType")) {
                    alert.setMsgType(value);
                }
                if (qname.equals("source")) {
                    alert.setSource(value);
                }
                if (qname.equals("restriction")) {
                    alert.setRestriction(value);
                }
                if (qname.equals("addresses")) {
                    alert.setAddresses(value);
                }
                if (qname.equals("code")) {
                    alert.addCode(value);
                }
                if (qname.equals("note")) {
                    alert.setNote(value);
                }
                if (qname.equals("references")) {
                    alert.setReferences(value);
                }
                if (qname.equals("incidents")) {
                    alert.setIncidents(value);
                }

                if (qname.equals("language")) {
                    info.setLanguage(value);
                }
                if (qname.equals("category")) {
                    info.addCategory(value);
                }
                if (qname.equals("event")) {
                    info.setEvent(value);
                }
                if (qname.equals("responseType")) {
                    info.addResponseType(value);
                }
                if (qname.equals("urgency")) {
                    info.setUrgency(value);
                }
                if (qname.equals("severity")) {
                    info.setSeverity(value);
                }
                if (qname.equals("certainty")) {
                    if (value.equals("Very Likely")) {
                        value = "Likely";
                    } // for backward compatibility with CAP 1.0
                    info.setCertainty(value);
                }
                if (qname.equals("audience")) {
                    info.setAudience(value);
                }
                if (qname.equals("valueName")) {
                    valuePair.setValueName(value);
                }
                if (qname.equals("value")) {
                    valuePair.setValue(value);
                }
                if (qname.equals("eventCode")) {
                    info.addEventCode(valuePair.getValueName(), valuePair.getValue());
                }
                if (qname.equals("effective")) {
                    info.setEffective(value);
                }
                if (qname.equals("onset")) {
                    info.setOnset(value);
                }
                if (qname.equals("expires")) {
                    info.setExpires(value);
                }
                if (qname.equals("senderName")) {
                    info.setSenderName(value);
                }
                if (qname.equals("headline")) {
                    info.setHeadline(value);
                }
                if (qname.equals("description")) {
                    info.setDescription(value);
                }
                if (qname.equals("instruction")) {
                    info.setInstruction(value);
                }
                if (qname.equals("web")) {
                    info.setWeb(value);
                }
                if (qname.equals("contact")) {
                    info.setContact(value);
                }
                if (qname.equals("parameter")) {
                    info.addParameter(valuePair.getValueName(), valuePair.getValue());
                }

                if (qname.equals("resourceDesc")) {
                    res.setResourceDesc(value);
                }
                if (qname.equals("mimeType")) {
                    res.setMimeType(value);
                }
                if (qname.equals("size")) {
                    res.setSize(value);
                }
                if (qname.equals("uri")) {
                    res.setUri(value);
                }
                if (qname.equals("derefUri")) {
                    res.setDerefUri(value);
                }
                if (qname.equals("digest")) {
                    res.setDigest(value);
                }

                if (qname.equals("areaDesc")) {
                    area.setAreaDesc(value);
                }
                if (qname.equals("polygon")) {
                    area.addPolygon(value);
                }
                if (qname.equals("circle")) {
                    area.addCircle(value);
                }
                if (qname.equals("geocode")) {
                    area.addGeocode(valuePair.getValueName(), valuePair.getValue());
                }
                if (qname.equals("altitude")) {
                    area.setAltitude(value);
                }
                if (qname.equals("ceiling")) {
                    area.setCeiling(value);
                }

            }

        }
    }
}
